#include "BleedThroughTrinarisation.hpp"



#include <ise/iseQuantificationThresholderOtsu.hpp>
#include <ise/iseQuantificationThresholderOtsu1T.hpp>
#include <ise/iseGrayScaleHistogram.hpp>
#include <math.h>


//#include <iostream>





ConditionOnRectoAndVersoSameRegion::ConditionOnRectoAndVersoSameRegion(const girl::tools::Image &recto, const girl::tools::Image &verso)
    : _recto(recto),
    _verso(verso)
{}

bool ConditionOnRectoAndVersoSameRegion::same(const girl::point &p, girl::direction dir)
{
    int x = p.x();
    int y = p.y();
    if(dir == 2 )
           x = p.x() - 1;
    if(dir == 0 )
           x = p.x() + 1;

    if(dir  == 1 )
           y = p.y() - 1;

    if(dir == 3 )
           y = p.y() + 1;


    return !(condition(p.x(),p.y())
        || condition(x,y));
}

BleedThroughSameRegion::BleedThroughSameRegion(const girl::tools::Image &recto, const girl::tools::Image &verso,
                                               float rectoBackThd, float rectoInkThd, float versoBackThd)
                                                   : ConditionOnRectoAndVersoSameRegion(recto,verso),
                                                   _rectoBackThd (rectoBackThd),
                                                   _rectoInkThd (rectoInkThd),
                                                   _versoBackThd (versoBackThd)
{}




bool BleedThroughSameRegion::estTransparence(int i, int j)
{
    if( _recto.getPixel(i,j).a() > _rectoInkThd && _recto.getPixel(i,j).a() < _rectoBackThd
        /* Pas de text */                              /*Ni de fond*/
        && _verso.getPixel(i,j).a() < _versoBackThd
        /* Pas de fond */
        && _recto.getPixel(i,j).a() > _verso.getPixel(i,j).a())
    {

        float s = _recto.getPixel(i,j).a();
        float u  = _verso.getPixel(i,j).a();
        double t = (((s-u)*(s-u)) / (_rectoInkThd*_rectoInkThd));
        return (t > 1);
    }
    return false;
}

bool BackgroundSameRegion::condition(int i, int j)
{
    if( _recto.getPixel(i,j).a() > _rectoInkThd && _recto.getPixel(i,j).a() < _rectoBackThd
        && _verso.getPixel(i,j).a() < _versoBackThd
        && _recto.getPixel(i,j).a() > _verso.getPixel(i,j).a())
    {

        float s = _recto.getPixel(i,j).a();
        float u  = _verso.getPixel(i,j).a();
        double t = (((s-u)*(s-u)) / (_rectoInkThd*_rectoInkThd));
        return !(t > 1);
    }
    else
        return !(_recto.getPixel(i,j).a() < _rectoInkThd);
}



bool InkSameRegion::condition(int i, int j)
{
    if(_recto.getPixel(i,j).a() > _verso.getPixel(i,j).a())
    {
        return false;
    }
    else
        return (_recto.getPixel(i,j).a() < _rectoInkThd+20);
}


/***************************************************************************************************/
/***************************************************************************************************/
/***************************************************************************************************/
/**********************************BleedThroughTrinarisation****************************************/
/***************************************************************************************************/
/***************************************************************************************************/
/***************************************************************************************************/

const girl::tools::Pixel BleedThroughTrinarisation::BLEED_THROUGH(255,0,0);
const girl::tools::Pixel BleedThroughTrinarisation::INK(0,0,0);
const girl::tools::Pixel BleedThroughTrinarisation::BACKGROUND(255,255,255);

BleedThroughTrinarisation * BleedThroughTrinarisation::_instance = NULL;
const girl::tools::Image * BleedThroughTrinarisation::rectoPtr = NULL;



BleedThroughTrinarisation::BleedThroughTrinarisation(const girl::tools::Image &recto,const  girl::tools::Image &verso)
    :
    _bleedThroughToger(recto.width(),recto.height()),
    _backgroundToger(recto.width(),recto.height()),
    _inkToger(recto.width(),recto.height()),
    _recto (recto),
    _verso (verso)

{
    std::cout << ">>>>>>>>>>>>>>>>>> Trinarized" << std::endl;

    if(_recto.height() != _verso.height()
        || _recto.width() != _verso.width())
        throw Exceptions::NotSameSizeException(recto,verso);

    _trinarized = _recto;
    for(int i = 0; i < _recto.width(); i++)
    {
        for(int j = 0; j < _recto.height(); j++)
        {
            _trinarized.setPixel(i,j,_recto.getPixel(i,j));
        }
    }

}

BleedThroughTrinarisation * BleedThroughTrinarisation::reset(const girl::tools::Image &recto,
                                                             const girl::tools::Image &verso)
{
    delete _instance;
    _instance = NULL;
    _instance = new BleedThroughTrinarisation(recto,verso);

    return _instance;
}

BleedThroughTrinarisation * BleedThroughTrinarisation::instance()
{
    return _instance;
}

void BleedThroughTrinarisation::kill ()
  {
    if (NULL != _instance)
      {
        delete _instance;
        _instance = NULL;
      }
  }


BleedThroughTrinarisation::~BleedThroughTrinarisation()
{
}

girl::tools::Image BleedThroughTrinarisation::trinarized()
{


    int rectoInkthd = getRectoInkThreshold();
    int versoBackThd = getVersoBackgroundThreshold();
    int rectoBackThd = getRectoBackgroundThreshold(versoBackThd);


    //std::cout << rectoBackThd  << std::endl;
    //std::cout << rectoInkthd  << std::endl;
    //std::cout << versoBackThd  << std::endl;

    girl::regionDeque bleedThroughRegions;
    BleedThroughSameRegion bleedThroughRegionsSplitter(_recto,_verso, rectoBackThd, rectoInkthd, versoBackThd);
    _bleedThroughToger.splitRegion(1,bleedThroughRegionsSplitter,bleedThroughRegions);

    girl::regionDeque inkRegions;
    InkSameRegion inkRegionsSplitter(_recto,_verso, rectoBackThd, rectoInkthd, versoBackThd);
    _inkToger.splitRegion(1,inkRegionsSplitter,inkRegions);

    girl::regionDeque backgroundRegions;
    BackgroundSameRegion backgroundRegionsSplitter(_recto,_verso, rectoBackThd, rectoInkthd, versoBackThd);
    _backgroundToger.splitRegion(1,backgroundRegionsSplitter,backgroundRegions);

    //std::cout<< "Done " << std::endl;

    colorize(_backgroundToger,BACKGROUND);
    colorize(_bleedThroughToger,BLEED_THROUGH);
    colorize(_inkToger,INK);

    //applyMedianFilter(_trinarized);
    return _trinarized;
}


float BleedThroughTrinarisation::getBackGroundThreshold(const girl::tools::Image &image) const
{
    ise::GrayScaleHistogram h;
    h.reset(image,0,0);

    ise::QuantificationThresholderOtsu otsuLocal;
    std::vector<int> levels;
    otsuLocal.getLevels(h,levels);


    int s =0;
    int nb = 0;
    for(int i = 0; i < _recto.width(); i++)
    {
        for(int j = 0; j < _recto.height(); j++)
        {
            int pixel =  image.getPixel(i,j).a();
            if(pixel > levels[1])
            {
                s+= pixel;
                nb++;
            }
        }
    }

    return s/nb;
}


//TODO : Trouver une méthode adaptative !!! Mais apres ?
float BleedThroughTrinarisation::getRectoBackgroundThreshold(int rectoInkThd) const
{
    ise::GrayScaleHistogram h;
    h.reset(_recto,0,0);

    ise::QuantificationThresholderOtsu1T otsuLocal;
    std::vector<int> levels;
    otsuLocal.getLevels(h,levels);

    //std::cout << "Threshold Max "  << rectoInkThd << std::endl;
    //std::cout << "Threshold Max "  << levels[1] << std::endl;

    int max_gauche = 0;

    int hmax = 0;
    int hmax_index = 0;
    for(int i = 0; i <= h.getMax(); i++){
        if(h[i] > hmax && i > levels[1])
        {
            hmax = h[i];
            hmax_index = i;
           // std::cout << i << std::endl;
        }
    }

    //std::cout << "index" <<  hmax_index << std::endl;
    //std::cout << "level 1" <<  levels[1] << std::endl;
    //std::cout << "d" <<  (hmax_index-(255-hmax_index)) << std::endl;
    //ATTENTION A REVOIR
    int s_fondStart = levels[1] + ((hmax_index-(255-hmax_index)) - levels[1] )/2;

    std::vector<float> freq;
    for(int i = 0; i <= h.getMax(); i++){
        freq.push_back(h[i]);
    }
    /*Histogram *h2 = new Histogram("Histo COuler", Qt::red);
    h2->setFrequencies(freq);

    TVPlot *plot = new TVPlot();
    plot->populate(h2);
    QPixmap pix = myPixmap = QPixmap::grabWindow(plot->winId());
    */



    //std::cout << "s" <<  s_fondStart << std::endl;

    /*int s =0;
    int nb = 0;
    int max = 0;
    for(int i = 0; i < _recto.width(); i++)
    {
        for(int j = 0; j < _recto.height(); j++)
        {
            int pixel =  _recto.getPixel(i,j).a();

            if(_verso.getPixel(i,j).a() < levels[0] && pixel > levels[0] && pixel <= hmax_index)
            {
                s+=pixel;
                nb++;

            }
        }
    }*/
    /*if(nb == 0)
        return 200;*/

    return s_fondStart;
}

float BleedThroughTrinarisation::getVersoBackgroundThreshold() const
{
    return getBackGroundThreshold(_verso);
}

float BleedThroughTrinarisation::getRectoInkThreshold() const
{
    ise::GrayScaleHistogram h;
    h.reset(_recto,0,0);

    ise::QuantificationThresholderOtsu otsuLocal;
    std::vector<int> levels;
    otsuLocal.getLevels(h,levels);

    int s_chars=0;
    int nb_chars=0;
    for(int i = 0; i < _recto.width(); i++)
    {
        for(int j = 0; j < _recto.height(); j++)
        {
            int pixel =  _recto.getPixel(i,j).a();
            if(pixel <= levels[0])
            {
                s_chars+= pixel;
                nb_chars++;
            }
        }
    }

    return s_chars/nb_chars + ((levels[0] - s_chars/nb_chars) /2);

}

void BleedThroughTrinarisation::colorize(const girl::toger &tog, const girl::tools::Pixel &coul)
{
    girl::regionDeque regions;

    tog.allRegions(regions);

    girl::regionDeque::const_iterator it = regions.begin();
    girl::regionDeque::const_iterator itEnd = regions.end();

    for(; it != itEnd; ++it)
    {
        std::vector<girl::point> points;
        tog.regionDomain(*it,points);
        for(int i= 0; i < points.size() ; i++)
        {
            _trinarized.setPixel(points[i].x(),points[i].y(), coul);
        }
    }
}

void BleedThroughTrinarisation::applyMedianFilter(girl::tools::Image &pImage)
{
    for (int j = 0; j < pImage.height(); ++j) {
        for (int i = 0; i < pImage.width(); ++i) {

            int s_a=0;
            int s_b=0;
            int s_c=0;
            int nb =0;
            for (int x = i - 1; x <= i + 1; x++) {
                for (int y = j - 1; y <= j + 1; y++) {
                    if (x > 0 && x < pImage.width()
                        && y > 0 && y < pImage.height()){

                        nb++;
                        s_a+=pImage.getPixel(x, y).a();
                        s_b+=pImage.getPixel(x, y).b();
                        s_c+=pImage.getPixel(x, y).c();
                    }
                }
            }
            if(nb>0){
                int a = s_a/nb;
                int b = s_b/nb;
                int c = s_c/nb;
                pImage.setPixel(i, j, girl::tools::Pixel(a,b,c));
            }

        }
    }

}
